/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fyx.linker.web;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.component.UIComponent;
import org.fyx.core.context.FyxContextFactory;
import org.fyx.linker.controll.LinkerCategoryFacadeLocal;
import org.fyx.linker.controll.LinkerLinkFacadeLocal;
import org.fyx.linker.entity.LinkerCategory;
import org.fyx.linker.entity.LinkerLink;
import org.fyx.linker.web.category.CategoryTreeNode;
import org.fyx.mula.ejb.MulaLocal;
import org.fyx.web.context.WebContext;
import org.primefaces.component.menuitem.MenuItem;
import org.primefaces.component.submenu.Submenu;
import org.primefaces.model.DefaultMenuModel;
import org.primefaces.model.MenuModel;

/**
 * Trida zodpovida za spravnou funkci tvorby vsech odkazu v systemu.
 * Vytvari odkazy podle vnitrnich pravidel.
 * Vytvari viceurovnove uzivatelske menu.
 * 
 * @author xljan
 */
@Stateless
public class LinkerBean implements LinkerLocal {
    public static final String URL_DELIMITER = ",";
    private static final String INTERNAL_BASE_URL = 
            "http://localhost:8080/FyxWeb/";
    private static final String INTERNAL_TPL_URL = 
            "http://localhost:8080/FyxWeb/faces/templates/";
    /** Znaky ktere nepatri do url **/
    private static final String[] key = {" ", "&", "/"};
    /** Znaky kterymi budou nahrazeny **/
    private static final String[] rep = {"-", "-", "-"};

    @EJB
    private LinkerLinkFacadeLocal linkerLinkFacade;
    @EJB
    private LinkerCategoryFacadeLocal linkerCategoryFacade;
    @EJB
    private MulaLocal mula;

    /**
     * Vrati odkaz dle nastavenych pravidel na konkretni stranku.
     * @param link relativni url stranky ve formatu modul/patch/stranka.xhtml
     * @return odkaz
     */
    @Override
    public String getLink(String link) {
        if(link.contains(INTERNAL_TPL_URL)){
            return getLinkAfterBase(link.replace(INTERNAL_TPL_URL, ""));
        } else if(link.equals(INTERNAL_BASE_URL)){
            return WebContext.getCurrentContext().getBaseUrl();
        }
        return getLinkAfterBase(link.replace(INTERNAL_TPL_URL, ""));
    }

    @Override
    public String convertForUrl(String string) {
        for(int i = 0; i < key.length; i++){
            string = string.replace(key[i], rep[i]);
        }
        return string;
    }
    
    private String getLinkAfterBase(String link){
        //TODO optimalizovat kod regularnim vyrazem na parsovani
        String lr[] = link.split("\\?", 2);
        String patch = lr[0].split("\\.")[0];
        String aParameters[] = null;
        String parameters = "";
        if (lr.length > 1) {
            aParameters = lr[1].replace("&", "=").split("\\=");
            for (int i = 1; i < aParameters.length; i += 2) {
                parameters += URL_DELIMITER + aParameters[i];
            }
        }
        String url = ((WebContext) FyxContextFactory.getContext()).getBaseUrl()
                + patch + parameters;
        return url;
    }

    /**
     * Prida link do menu. Pote se tento link zobrazuje v editoru
     * kategorii, kde se zacili.<br/>Atribut Value je preklad atributu id.<br/>
     * Priklad:<p>item = new MenuItem();<br/>
     *  item.setValue(mula.get("linker_category"));<br/>
     *  item.setIcon("ui-icon ui-icon-shuffle");<br/>
     *  item.setUrl(linker.getLink("linker/category/edit.xhtml"));<br/>
     *  item.setId("linker_category");<br/>
     *  linker.addMenuItem(item, LinkerRoles.CATEGORY_MANAGER);</p>
     * @param menuItem odkaz.
     * @param role uzivatelska role nutna pro zobrazeni odkazu
     */
    @Override
    public void addMenuItem(MenuItem menuItem, String role) {
        if (WebContext.getCurrentContext().isUserInRole(role)) {
            WebContext.getMapMenuItems().
                    put(menuItem.getId(), menuItem);
        }
        WebContext.getMapAllMenuItems().put(menuItem.getId(), menuItem);
    }

    /**
     * Prida link do menu. Pote se tento link zobrazuje v editoru
     * kategorii, kde se zacili.<br/>Atribut Value je preklad atributu id.<br/>
     * @param menuItem odkaz
     * @param 
     */
    /**
     * Prida link do menu. Pote se tento link zobrazuje v editoru
     * kategorii, kde se zacili.
     * @param value nazev odkazu je jeho ID
     * @param url url adresa odkazu dle vnitrnich pravidel viz. getLink
     * @param id ID odkazu stejne jako value
     * @param icon ikona
     * @param role uzivatelska role nutna pro zobrazeni odkazu 
     */
    @Override
    public void addMenuItem(String value, String url, String id, String icon,
            String role) {
        MenuItem menuItem = new MenuItem();
        menuItem.setValue(value);
        menuItem.setIcon(icon);
        menuItem.setUrl(url);
        menuItem.setId(id);
        addMenuItem(menuItem, role);
    }

    /**
     * Vytvori novy menu model. Pokud je menu model jiz vytvoren, prepise ho.
     * Nenutí znovuvytvoření položek v menu.
     */
    @Override
    public void createMenuModel() {
        MenuModel menuModel = new DefaultMenuModel();
        LinkerCategory root = linkerCategoryFacade.findRoot();
        if (root == null) {
            WebContext.setMenuModel(menuModel);
            return;
        }
        // Ziskani masky menuItem polozek, kde klic je jeji ID MenuItem.getId()
        // Slouzi jako mapa kde se bude provadet prunik maskMenu a databazovych
        // zaznamu
        Map<String, MenuItem> maskMenu = new HashMap<String, MenuItem>(
                WebContext.getMapMenuItems());
        Submenu rootSubmenu = createSubmenuCascade(root, maskMenu);
        removeEmptySubmenus(rootSubmenu);
        for (UIComponent component : rootSubmenu.getChildren()) {
            if (component instanceof MenuItem) {
                menuModel.addMenuItem((MenuItem) component);
            }
            if (component instanceof Submenu) {
                menuModel.addSubmenu((Submenu) component);
            }
        }
        WebContext.setMenuModel(menuModel);
    }

    /**
     * Spusti vsechny observery pro udalost vytvoreni menu a vytvori menu.
     */
    @Override
    public void recreateMenuModel() {
        WebContext.getMapMenuItems().clear();
        WebContext.getMapAllMenuItems().clear();
        WebContext.getMenuCreatingEventHandler().notifyObservers();
        createMenuModel();
    }

    /**
     * Vytvori kaskadove menu od korene.
     * Maska s odkazy zabezpecuje, aby se nevytvarely duplicitni odkazy
     * a aby se vytvorily jen ty odkazy na ktere ma uzivatel pravo.
     * @param category koren
     * @param maskItems maska s odkazy
     * @return strom kde uzly jsou UIComponents
     */
    private Submenu createSubmenuCascade(LinkerCategory category,
            Map<String, MenuItem> maskItems) {
        Submenu submenu = new Submenu();
        submenu.setLabel(mula.get(category.getName()));
        Collection<LinkerCategory> children = category.getLinkerCategoryCollection();
        for (LinkerCategory child : children) {
            submenu.getChildren().add(createSubmenuCascade(child, maskItems));
        }
        Collection<LinkerLink> links = category.getLinkerLinkCollection();
        for (LinkerLink link : links) {
            MenuItem menuItem = maskItems.remove(link.getIdLink());
            if (menuItem != null) {
                submenu.getChildren().add(menuItem);
            }
        }
        return submenu;
    }

    /**
     * Odebere vsechny submenu, ktere nemaji zadne potomky typu 
     * MenuItem (odkazy) a vrati pocet odkazu v submenu.
     * @param submenu koren stromu submenu
     * @return pocet vsech MenuItem, i zanorenych
     */
    private int removeEmptySubmenus(Submenu submenu) {
        int noMenuItems = 0;
        // Je nutne zkopirovat potomky z duvodu konkurencniho pristupu
        List<UIComponent> children =
                new ArrayList<UIComponent>(submenu.getChildren());
        for (UIComponent child : children) {
            if (child instanceof MenuItem) {
                noMenuItems++;
            }
            if (child instanceof Submenu) {
                noMenuItems += removeEmptySubmenus((Submenu) child);
            }
        }
        // Pokud submenu neobsahuje zadne odkazy je odebrano
        if (noMenuItems < 1 && submenu.getParent() != null) {
            submenu.getParent().getChildren().remove(submenu);
        }

        return noMenuItems;
    }

    @Override
    public void updateMenu(CategoryTreeNode root) {
        linkerLinkFacade.removeAll();
        linkerCategoryFacade.removeAll();
        root.synchronizeCascade();
        linkerCategoryFacade.create(root.getCategory());
    }

    @Override
    public void removeMenuItem(String idMenuItem) {
        linkerLinkFacade.remove(new LinkerLink(idMenuItem));
    }
}